const path = require('path')
const fs = require('fs');
const { defaultsDeep } = require("lodash");
const defaultOptions = require("./options");
const PluginAPI = require("./PluginAPI");
const logStep = require("../../util/logStep");
const logInfo = require('../../util/logInfo');
module.exports = class Service {
    constructor(context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
        logStep(
            "Service constructor 主要执行了 pkg 的获取和 plugins 的设置。 特别 plugins 是 Service 的灵魂所在"
        );
        logInfo('保存 this 到 process.VUE_CLI_SERVICE 中，其他插件中会用到')
        process.VUE_CLI_SERVICE = this;
        logInfo('其他初始化参数')
        this.initialized = false;
        this.context = context;
        this.inlineOptions = inlineOptions;
        this.webpackChainFns = [];
        this.webpackRawCOnfigFns = [];
        this.devServerConfigFns = [];
        this.commands = {};
        this.pkgContext = context;

        logInfo('加载pkg')
        this.pkg = this.reslovePkg(pkg);
        logInfo('加载用到的插件，包括service内置插件和package.json 中配置的其他插件, 这里是 service 插件机制的入口部分');
        this.plugins = this.reslovePlugins(plugins);
        this.pluginsToSkip = new Set();

        logStep(
            "获取配置的不同命令下的mode，正常开发中需要从 plugin 中获取 module.exports.defaultModes。"
        );
        this.modes = {
            build: "procedure",
            serve: "development",
        };
    }


    async run(name, args = {}, rawArgv = {}) {
        logStep("根据命令获取 mode， 如果是 build+watch时 固定为 development");
        const mode =
            args.mode ||
            (name === "build" && args.watch ? "development" : this.modes[name]);
        logInfo(
            "有些插件时再 init 时执行，有些是再 init 之后执行的。这时就需要配置在 skips 中"
        );
        this.init(mode);

        args._ = args._ || [];
        let command = this.commands[name];
        if (!command && name) {
            logError("不存在的命令");
            process.exit(1);
        }
        logStep("取到命令后开始执行命令：" + name);
        const { fn } = command;
        return fn(args, rawArgv);
    }

    init(mode = process.env.VUE_CLI_MODE) {
        if (this.initialized) {
            //懒加载
            return;
        }
        this.initialized = true;
        this.mode = mode;
        if (mode) {
            this.loadEnv(mode);
        }
        const userOptions = this.loadUserOptions();
        this.projectOptions = defaultsDeep(userOptions, defaultOptions);

        logStep(
            "这里开始调用所有插件的 apply 方法安装插件，这里和 @vue/cli 里的 Generator+GeneratorAPI 的设计思想一致"
        );
        logStep("这里也是真正的执行命令开始调用的地方。");
        console.log(this.plugins);
        this.plugins.forEach(({ id, apply }) => {
            if (this.pluginsToSkip.has(id)) return;
            apply(new PluginAPI(id, this), this.projectOptions);
        });

        logInfo(
            "安装插件后会向 this.commands 中注册命令，只有这里注册过了，后边才可以使用。"
        );
        logStep("webpack 命令的合并");
        logInfo(
            "this.webpackChainFns.push(this.projectOptions.chainWebpack)  + this.webpackChainFns.push(this.projectOptions.configureWebpack) "
        );
        if (this.projectOptions.chainWebpack) {
            this.webpackChainFns.push(this.projectOptions.chainWebpack);
        }
        if (this.projectOptions.configureWebpack) {
            this.webpackChainFns.push(this.projectOptions.configureWebpack);
        }
    }

    loadEnv(mode) {
        logStep(`利用 dotenv-expand 将 [mode:${mode}] 加载到环境变量中`);
    }

    loadUserOptions() {
        logStep("从 vue.config.js 中获取用户配置。");
        return {};
    }

    reslovePkg(inlinePkg, context = this.context) {
        if (inlinePkg) {
            return inlinePkg;
        } else if (fs.existsSync(path.resolve(context, 'package.json'))) {

        }
    }

    readPkg() {
        logInfo('从 package.json 中读取 json 内容')
        const pkgPath = path.resolve(context, 'package.json')
        const pkgJSON = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
        logInfo('调用 normalize-package-data 对 pkgJSON 的元数据进行标准化处理/格式化')
        return pkgJSON;
    }

    reslovePlugins(inlinePlugins, useBuilltIn) {
        logStep(
            "解析拼接内部插件，以及是否使用 useBuiltIn 等其他插件,这里是 vue-cli-service 插件系统的核心入口"
        );
        logInfo("这里也说明了 service 内部也采用了 plugin 的模式来实现功能的堆叠");
        logInfo("例如：./commands/serve ./command/build ./commands/inspect");

        const idToPlugin = (id) => ({
            id: id.replace(/^.\//, "built-in:"),
            apply: require(id),
        });

        const buildInPlugins = [
            //   "./commands/serve",
            "./commands/build",
            //   "./commands/inspect",
            //   "./commands/help",
        ].map(idToPlugin);

        if (inlinePlugins) {
            logStep(`加载其他默认的插件 ${inlinePlugins}`);
            return buildInPlugins.concat(inlinePlugins);
        } else {
            logStep(
                `加载 package 中配置的插件：${this.pkg.devDependencies
                },${JSON.stringify(this.pkg.dependencies)}`
            );
            logInfo(
                "这里就是为什么cli-service 可以加载pkg 中的插件，而无需额外配置的原因。"
            );
            logInfo(
                "是否是cli-service的插件依靠命名来确定：/^(@vue/|vue-|@[w-]+/vue-)cli-plugin-/"
            );
            const projectPlugins = Object.keys(this.pkg.devDependencies || {})
                .concat(Object.keys(this.pkg.dependencies || {}))
                .filter((id) => {
                    return /^(@vue\/|vue-|@[\w-]+\/vue-)cli-plugin-/.test(id);
                })
                .map((id) => {
                    let apply = () => { };
                    try {
                        apply = require(id);
                    } catch (error) { }
                    return { id, apply };
                });
            logInfo(projectPlugins);
            return buildInPlugins.concat(projectPlugins);
        }
    }
}